package aquarium.gui;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.util.*;

import javax.swing.JPanel;

import aquarium.factory.*;
import aquarium.factory.FishType;
import aquarium.items.AquariumItem;
import aquarium.items.Bubble;
import aquarium.items.Fish;
import aquarium.items.MobileItem;
import aquarium.items.Sex;
import aquarium.time.TManager;
import aquarium.util.RandomNumber;


public class Aquarium extends JPanel {

	private static final long serialVersionUID = 1L;

	public static int SIZE_AQUA_X = 700, SIZE_AQUA_Y = 700;
	private static int PERCENT_FERTILIZATION = 5;
	private static int NB_MAX_FISH = 42;


	private Image _background = null;
	private Graphics _gContext = null;

	private final URL _url = ClassLoader.getSystemResource("image/aquarium_carre_reduis.png");
	private Image _imageFenetre = Toolkit.getDefaultToolkit().createImage(_url);

	private Collection<AquariumItem> items = new ArrayList<AquariumItem>();
	private HashMap<Integer, ArrayList<AquariumItem>> _list_packet_aquarium_items = new HashMap<Integer, ArrayList<AquariumItem>>();

	private TManager tm = new TManager(items, this);

	private StoneFactory sf = new StoneFactory();
	private SeaweedFactory swf = new SeaweedFactory();
	private FishFactory ff = new FishFactory(tm);
	private BubbleFactory bf = new BubbleFactory(tm);

	public boolean modified = false;

	public Aquarium() {

	}

	public void start() {
		tm.startAll();
	}

	public Collection<AquariumItem> neighbourhood(AquariumItem item, int r) {
		return null;
	}

	public void addFish(Collection<AquariumItem> items, FishType type){
		switch(type){
		case Normal : 
			AquariumItem fn = ff.newItems(FishType.Normal);
			ff.sink(items, fn);
			items.add(fn);
			break;
		case OneSecond :
			AquariumItem fo = ff.newItems(FishType.OneSecond);
			ff.sink(items, fo);
			items.add(fo);
			break;
		case Piscivore :
			AquariumItem fp = ff.newItems(FishType.Piscivore);
			ff.sink(items, fp);
			items.add(fp);
			break;
		default : break;
		}
	}

	public void addStone(Collection<AquariumItem> items){
		AquariumItem ai = sf.newItems();
		sf.sink(items, ai);
		items.add(ai);
	}

	public void addSeaweed(Collection<AquariumItem> items){
		AquariumItem ai = swf.newItems();
		swf.sink(items, ai);
		items.add(ai);
	}

	public void add_bubble(Collection<AquariumItem> items, AquariumItem fish_killer, AquariumItem fish_victim){
		AquariumItem ai = bf.newItems();
		((Bubble)ai).init_bubble(fish_killer, fish_victim, getSizeX(), getSizeY());
		items.add(ai);
	}

	public void add_egg(){
		synchronized(items){
			Stack<FishType> nb_fish_couple = new Stack<FishType>();
			for(AquariumItem ai : items) {
				if(ai instanceof Fish){
					FishType type = ((FishType)ai.egg_possibility(items)); // Regarde s'il peut y avoir des oeufs
					if(type != null && ((Fish)ai).get_sex() == Sex.Female)
						nb_fish_couple.push(type);
				}
			}
			while(!nb_fish_couple.empty()){
				creat_egg(nb_fish_couple.pop()); // Cree un certains nombre de poisson completement hasardeux
			}
		}
	}

	public void creat_egg(FishType type){
		synchronized (items) {
			int nb_baby = RandomNumber.randomValue(0, 2);
			int success_baby;
			if (items.size() < NB_MAX_FISH){
				for(int i = 0; i < nb_baby; i++){
					if (_list_packet_aquarium_items.size() <= 40){
						success_baby = RandomNumber.randomValue(0, 100);
						if(success_baby <= PERCENT_FERTILIZATION){
							addFish(items, type);
						}
					}
				}
			}
		}
	}

	public void creat_bubble(AquariumItem fish_killer, AquariumItem fish_victim){
		synchronized(items){
			add_bubble(items, fish_killer, fish_victim);
		}
	}

	public void randomStoneAndSeaweedGenerator(Collection<AquariumItem> items){
		int nb_Stone = RandomNumber.randomValue(0, 4);
		int nb_Seaweed = RandomNumber.randomValue(0, 4);

		for(int i=0; i<nb_Stone; i++)
			addStone(items);
		for(int i=0; i<nb_Seaweed; i++)
			addSeaweed(items);
	}

	public void randomFishGenerator(Collection<AquariumItem> items){
		synchronized(items){
			int nb_Normal = RandomNumber.randomValue(0, 7);
			int nb_OneSecond= RandomNumber.randomValue(0, 5);
			int nb_Piscivore = RandomNumber.randomValue(0, 5);

			for(int i=0; i<nb_Normal; i++)
				addFish(items, FishType.Normal);
			for(int i=0; i<nb_OneSecond; i++)
				addFish(items, FishType.OneSecond);
			for(int i=0; i<nb_Piscivore; i++)
				addFish(items, FishType.Piscivore);
		}
	}

	public Collection<AquariumItem> getCollection(){
		return items;
	}


	//**************************
	//  GRAPHIQUE
	//**************************
	public int getSizeX() {
		return SIZE_AQUA_X;
	}
	public int getSizeY() {
		return SIZE_AQUA_Y;
	}

	public void getdraw(){
		draw();
	}

	public Graphics getGraphic(){
		return _gContext;
	}

	public void paint(Graphics g) {
		g.drawImage(_background, 0, 0, this);
	}

	public TManager get_Tmanager(){
		return tm;
	}

	public HashMap<Integer, ArrayList<AquariumItem>> get_hashmap_list(){
		return _list_packet_aquarium_items;
	}

	/*
	 * Bufferisation de l'image
	 */

	//* Version avec une image de fond
	private void draw() {
		if (this._gContext == null){
			prepareImage(_imageFenetre, this);
			_background = createImage(SIZE_AQUA_X, SIZE_AQUA_Y);

			while(_background == null){ // Pour �tre s�r que background existe pour l'utiliser
				prepareImage(_background, this);
				checkImage(_background, null);
			}

			while(!_background.getGraphics().drawImage(_imageFenetre,0,0,this)) {
				_background.getGraphics().drawString("Image en cours de chargement...",20,128);
			}
			_background.getGraphics().drawImage(_imageFenetre, 0, 0, SIZE_AQUA_X, SIZE_AQUA_Y, null);
			prepareImage(_background, this);
			checkImage(_background, null);
			_background.getGraphics().dispose();

			if(_background!=null)
				this._gContext = _background.getGraphics();
		} else {

			_background.getGraphics().drawImage(_imageFenetre, 0, 0, SIZE_AQUA_X, SIZE_AQUA_Y, null);
			_gContext = _background.getGraphics();


			ajuster_collection();
			synchronized(items){
				Iterator<AquariumItem> it = items.iterator();
				Stack<AquariumItem> pile = new Stack<AquariumItem>();
				while (it.hasNext()) 
				{
					AquariumItem ai = it.next();
					if(ai.must_be_destroyed()) // Si l'item doit etre detruit
						pile.push(ai); // On ne le dessine pas
					else
						ai.changeSize('s');
					ai.draw(_gContext); // Sinon on le dessine

				}
				while (!pile.empty())
					items.remove(pile.pop()); // Et on le supprime de la collection d'item
			}
		}

		this.repaint();
	}

	public void ajuster_collection(){
		synchronized(items){
			Iterator<AquariumItem> it = items.iterator();
			Stack<AquariumItem> pile = new Stack<AquariumItem>();
			while (it.hasNext()) 
			{
				AquariumItem ai = it.next();
				if(ai instanceof MobileItem && ((MobileItem)ai).is_visitor()){
					pile.push(ai);
				}
			}
			while (!pile.empty())
				items.remove(pile.pop());


			synchronized(_list_packet_aquarium_items){
				for(int i = 0; i < _list_packet_aquarium_items.size()+1; i++){
					if (_list_packet_aquarium_items.containsKey(i)){
						Iterator<AquariumItem> ite = _list_packet_aquarium_items.get(i).iterator();
						while (ite.hasNext()) 
						{
							AquariumItem ai = ite.next();
							items.add(ai);
						}
					}
				}
			}
		}
	}
}